package com.cskaoyan.javase._2polymorphic._2advantage;

/**
 * 多态是面向对象三大特征之一，它基于继承而实现，是面向对象思想的集大成者。
 * 接下来我们研究一下，它的优缺点。
 * 我们通过一个案例：
 * 科学家要收集动物的叫声去研究，先要收集猫，狗两种动物的叫声研究。
 * 随后又扩展到其它动物，最后又需要研究人类。
 *
 * 多态的优点:
 *      1.要实现多态必须要继承，而继承提高了代码复用率，
 *          提升了程序的可维护性。（继承保证）
 *      2.有了多态后，用父类引用指向不同子类对象，只需要调用同名方法
 *          就能自动根据不同子类对象，得出不同的行为。
 *          这大幅度简化了代码，提升了程序的扩展性。（多态保证）
 *
 * 现在科学家已经把很多动物都已经研究过了,现在想要研究人类的叫声
 * 但是人比较特殊,动物抓一只过来就让它
 * 但是人不能抓过来,要想让人类配合研究,需要加钱
 * 所以人类在叫之前,需要特殊的行为"加钱"
 *
 * 多态的缺点:
 *      由于多态中，使用父类引用指向子类对象，
 *      父类引用限制了子类对象的功能，这意味着：
 * 子类独有的成员是无法使用父类引用访问的。
 *
 * 强制类型转换的语法:
 * 1.强转/引用数据类型能够做类型转换的前提是存在继承
 *      而且必须发生在同一条继承链中的两个类之间
 *      不符合上述条件,会导致编译报错
 * 2.强转必须把父类引用,强转成它的子类引用
 *      否则就会编译报错,
 *      这是能够通过编译的前提条件.
 * 3.能够通过编译的强转,并不意味着就一定能够强转成功
 * 当代码里出现不合适的强转时,会导致程序报错
 * 抛出异常:
 * ClassCastException(类型转换异常)
 * 所以向下转型,强制类型转换,代码能够正常运行的条件是什么呢?
 * 强转后的引用,要真的能够指向父类引用所指向的对象,这要求:
 *      强转后的引用要么就是父类引用指向的对象的类型
 *      要么就是父类引用指向的对象的父类类型
 *
 * 强转/向下转型是很危险的,稍有不慎就会导致程序报错终止,抛出异常
 * 那么有没有办法/语法来判断是否能够做强转呢?
 * 有
 * 使用instanceof关键字
 * 使用方法:
 * 引用/对象名 instanceof 类名
 *
 * 以上语法结构用于判断,左边的引用所指向的对象,是否是后面类名的对象或者子类对象
 * 以上语法结够返回一个布尔值
 * 如果左边的引用指向的对象是右边类名的对象或者子类对象,那么返回true
 * 如果左边的引用指向的对象不是右边类名的对象或者子类对象,那么返回false
 * 当该语法结构,返回true时,表示左边的引用能够强转成后面类名的引用
 * 一般该语法结构配合if使用
 *
 * 特殊的:
 * null instanceof 任何类名
 * 都返回false
 *
 * 引用数据类型要成功"向下转型"，完成强转条件是很苛刻的，
 * 实际开发中，先慎重考虑是否真的需要强转。
 * 然后在做强转之前，也需要使用instanceof关键字判断，保障安全性。
 * 麻痹大意，导致抛出ClassCastException异常，就会很难受了。
 *
 * @since 16:11
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        // 方法形参中写引用数据类型,本质是需要传入一个对象
        // collectCatVoice(new Cat());

        collectAnimalVoice(new Cat());
        collectAnimalVoice(new Dog());
        collectAnimalVoice(new Pig());

        collectAnimalVoice(new Person());

        Animal a = null;
        System.out.println(a instanceof Pig);

    }


    /*
        写一个方法,用于收集研究所有动物的叫声
        只要是Animal的子类,都可以传入方法
     */
    public static void collectAnimalVoice(Animal a) {
        // 抓一只动物过来,然后让它叫
        /*
            现在这个引用a是一个父类Animal类的引用
            它能够访问子类独有行为吗?
            显然是不可以的,因为访问范围要看引用的类型
            父类的类型限制了子类对象的访问范围

            那么这里怎么解决呢?
            根源在于父类引用限制了访问范围
            所以这里需要做类型转换
            把父类引用 --> 子类引用
            子类引用就能够访问子类独有成员了.

            这个转换引用的过程
            父 --> 子
            属于向下转型,是强制类型转换
         */
        if (a instanceof Person){
            // 返回true表示能够强转
            Person p = (Person) a;
            p.addMoney();
        }
        a.shout();
        // 收集研究叫声
    }

    /*
        写一个方法,用于收集研究动物的叫声
        首先研究猫的叫声
     *//*
    public static void collectCatVoice(Cat c) {
        // 抓一只猫过来,然后让它叫
        c.shout();
        // 收集研究叫声
    }*/
}

class Animal {
    public void shout() {
        System.out.println("动物叫声");
    }
}

class Cat extends Animal {
    @Override
    public void shout() {
        System.out.println("喵喵喵~");
    }
}

class Dog extends Animal {
    @Override
    public void shout() {
        System.out.println("汪汪汪~");
    }
}

class Pig extends Animal {
    @Override
    public void shout() {
        System.out.println("猪叫~");
    }
}

class Person extends Animal {
    // 人类独有的行为
    public void addMoney() {
        System.out.println("给钱才叫!");
    }

    @Override
    public void shout() {
        System.out.println("嘤嘤嘤~");
    }
}